package com.thinkit.cms.service.admin;

import com.google.common.collect.Lists;
import com.thinkit.cms.api.admin.MenuService;
import com.thinkit.cms.api.admin.RoleMenuService;
import com.thinkit.cms.dto.admin.MenuDto;
import com.thinkit.cms.dto.admin.MetaDto;
import com.thinkit.cms.dto.admin.RoleMenuDto;
import com.thinkit.cms.entity.admin.Menu;
import com.thinkit.cms.mapper.admin.MenuMapper;
import com.thinkit.core.base.BaseServiceImpl;
import com.thinkit.core.constant.Constants;
import com.thinkit.core.constant.SecurityConstants;
import com.thinkit.core.handler.CustomException;
import com.thinkit.nosql.annotation.CacheClear;
import com.thinkit.nosql.base.BaseRedisService;
import com.thinkit.utils.enums.UserFrom;
import com.thinkit.utils.model.ApiResult;
import com.thinkit.utils.model.Tree;
import com.thinkit.utils.properties.PermitAllUrlProperties;
import com.thinkit.utils.utils.BuildTree;
import com.thinkit.utils.utils.Checker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * <p>
 * 菜单管理 服务实现类
 * </p>
 *
 * @author dl
 * @since 2018-03-21
 */
@Service
public class MenuServiceImpl extends BaseServiceImpl<MenuDto, Menu, MenuMapper> implements MenuService {

    @Autowired
    RoleMenuService roleMenuService;

    @Autowired
    BaseRedisService baseRedisService;

    @Autowired
    PermitAllUrlProperties permitAllUrlProperties;

    @Value("${spring.application.name}")
    private String application;

    @Transactional
    public boolean save(MenuDto menuDto) {
        this.ckLegal(menuDto);
        super.insert(menuDto);
        if (Checker.BeNotBlank(menuDto.getParentId()) && !"0".equals(menuDto.getParentId())) {
            List<RoleMenuDto> roleMenuDtos = roleMenuService.getsByMenuId(menuDto.getParentId());
            if (Checker.BeNotEmpty(roleMenuDtos)) {
                roleMenuDtos.forEach(roleMenuDto -> {
                    roleMenuDto.setHalfChecked(0);
                });
                roleMenuService.updateByPks(roleMenuDtos);
            }
        }
        clearPermsCacheByUid();
        return true;
    }

    private void ckLegal(MenuDto m){
        boolean menuType = Arrays.asList(0,1,2).contains(m.getType());
        if(!menuType){
            throw new CustomException(ApiResult.result(5009));
        }
        if(ckHasPerm(m.getPerms(),m.getId())){
            throw new CustomException(ApiResult.result(5010));
        }
        if(m.getType()==0){// 根目录
            m.setComponent("RouteView");// 目录的组件名称默认是 RouteView
            ckParamsIsNull(m.getPath(),5011);
        }else if(m.getType()==1){ // 菜单
            ckParamsIsNull(m.getPath(),5011);
            ckParamsIsNull(m.getComponent(),5012);
            ckParamsIsNull(m.getUrl(),5013);
        }
        else if(m.getType()==2){// 按钮
            ckParamsIsNull(m.getUrl(),5013);
        }
        m.setHidden(false);
    }

    private void ckParamsIsNull(String param,int code){
       if(Checker.BeBlank(param)){
           throw new CustomException(ApiResult.result(code));
       }
    }

    private boolean ckHasPerm(String perm,String id){
       Integer count= baseMapper.ckHasPerm(perm,id);
       return count>0;
    }


    @CacheClear(method = "loadMenu",key = "#clas.targetClass+'.'+#clas.method+'.*'")
    @Override
    public boolean update(MenuDto menuDto) {
        ckLegal(menuDto);
        clearPermsCacheByUid();
        return super.updateByPk(menuDto);
    }

    @Override
    public Tree<MenuDto> selectTreeList() {
        List<MenuDto> menus = baseMapper.selectTreeList();// listDtoByMap(Maps.newHashMap());
        if (menus != null && !menus.isEmpty()) {
            List<Tree<MenuDto>> trees = new ArrayList<Tree<MenuDto>>();
            for (MenuDto sysMenuDO : menus) {
                Tree<MenuDto> tree = new Tree<MenuDto>();
                tree.setId(sysMenuDO.getId());
                tree.setKey(sysMenuDO.getId());
                tree.setParentId(sysMenuDO.getParentId());
                tree.setName(sysMenuDO.getName());
                tree.setSpread(true);
                Map<String, Object> attributes = new HashMap<>(16);
                attributes.put("url", sysMenuDO.getUrl());
                attributes.put("icon", sysMenuDO.getIcon());
                attributes.put("perms", sysMenuDO.getPerms());
                attributes.put("type", sysMenuDO.getType());
                attributes.put("orderNum", sysMenuDO.getOrderNum());
                attributes.put("hidden", sysMenuDO.getHidden());
                tree.setAttributes(attributes);
                trees.add(tree);
            }
            Tree<MenuDto> t = BuildTree.build(trees);
            return t;
        } else {
            return null;
        }
    }

    @Override
    public List<MenuDto> selectMenuTreeByUid(String userId) {
        return baseMapper.selectMenuTreeByUid(userId);
    }


    @Cacheable(value= Constants.cacheName, key="#root.targetClass+'.'+#root.methodName+'.'+#p0+'.'+#p1",unless="#result == null")
    @Override
    public Set<String> selectPermsByUid(String userId,String application) {
        List<String> perms =baseMapper.selectPermsByUid(userId,application);
        Set<String> permsSet = new HashSet<>();
        if (Checker.BeNotEmpty(perms)) {
            for (String perm : perms) {
                if (Checker.BeNotBlank(perm)) {
                    permsSet.addAll(Arrays.asList(perm.trim().split(",")));
                }
            }
        }
        return permsSet;
    }

    @Override
    public void clearPermsCacheByUid() {
        baseRedisService.removeBlear(SecurityConstants.PERMISSION+Constants.delRedisKey);
        baseRedisService.removeBlear(Constants.cacheNameClear+this.getClass().toString()+".selectPermsByUid.*");
        baseRedisService.removeBlear(Constants.cacheNameClear+this.getClass().toString()+".loadMenu.*");
        String key= SecurityConstants.PERMISSION+ UserFrom.PLAT_USER.getClientId() +Constants.DOT+application;
        baseRedisService.remove(key);
    }




    @Override
    public List<MenuDto> selectMenuUid(String userId) {
        return baseMapper.selectMenuUid(userId);
    }

    @Override
    public MenuDto info(String id) {
        MenuDto chileMenuDto = getByPk(id);
        MenuDto parentMenuDto = getByPk(chileMenuDto.getParentId());
        if (Checker.BeNotNull(parentMenuDto)) {
            chileMenuDto.setParentName(parentMenuDto.getName());
        } else {
            chileMenuDto.setParentName("根目录");
        }
        return chileMenuDto;
    }

    @CacheClear(method = "loadMenu",key = "#clas.targetClass+'.'+#clas.method+'.*'")
    @Override
    public boolean delete(String id) {
        clearPermsCacheByUid();
        MenuDto menu = getByPk(id);
        if (menu.getType() == 0 || menu.getType() == 1) {//目录或者菜单需要递归删除
            List<String> ids = new ArrayList<>();
            ids.add(id);
            selectChilden(id, ids);
            boolean f = super.removeByIds(ids);
            if (f) {//删除菜单或者目录成功时将删除，角色-菜单关系表
                for (String menuId : ids) {
                    roleMenuService.deleteByMenuId(menuId);
                }
                return true;
            }
        } else if (menu.getType() == 2)//按钮
        {
            return super.removeById(id);
        }
        return false;
    }

    @Cacheable(value= Constants.cacheName, key="#root.targetClass+'.'+#root.methodName+'.'+#p0",unless="#result == null")
    @Override
    public List<MenuDto> loadMenu(String userId) {
        List<MenuDto> menuDtos  = baseMapper.loadMenu(userId);
        return filterMenu(menuDtos);
    }

    @Override
    public List<String> loadApplications() {
        return permitAllUrlProperties.applications();
    }

    @CacheClear(method = "loadMenu",key = "#clas.targetClass+'.'+#clas.method+'.*'")
    @Override
    public void hideIt(String id, Integer hidden) {
        if(Arrays.asList(0,1).contains(hidden)){
            baseMapper.hideIt(id,hidden);
        }
    }

    private List<MenuDto> filterMenu(List<MenuDto> menuDtos ){
        if(Checker.BeNotEmpty(menuDtos)){
            List<MenuDto> menuList = new ArrayList<>(16);
            for(MenuDto menuDto:menuDtos){
                MetaDto metaDto = new MetaDto();
                metaDto.setIcon(menuDto.getIcon()).setHidden(menuDto.getHidden()).setTitle(menuDto.getName()).
                setKeepAlive(menuDto.getKeepAlive());
                MenuDto menu = new MenuDto(metaDto);
                menu.setName(menuDto.getPath()).setComponent(menuDto.getComponent()).setParentId(menuDto.getParentId()).
                setRedirect(menuDto.getRedirect()).setKey(menuDto.getPath()).setIcon(menuDto.getIcon()).
                setHidden(menuDto.getHidden()).setId(menuDto.getId());
                menuList.add(menu);
            }
            return  menuList;
        }
        return Lists.newArrayList();
    }

    private void selectChilden(String id, List<String> list) {
        Map<String, Object> param = new HashMap<>(16);
        param.put("parent_id", id);
        List<MenuDto> menus = listDtoByMap(param);
        if (null != menus && !menus.isEmpty()) {
            for (MenuDto menu : menus) {
                list.add(menu.getId());
                selectChilden(menu.getId(), list);
            }
        }
    }

}
